package com.nightpiter.controller.algorithms;

import static com.nightpiter.controller.log.Logger.log;
import static com.nightpiter.controller.log.Logger.logProfilerBegin;
import static com.nightpiter.controller.log.Logger.logProfilerEnd;

import java.util.ArrayList;

import org.apache.harmony.luni.util.TwoKeyHashMap;

import com.nightpiter.model.MetroModelData;
import com.nightpiter.model.objects.Line;
import com.nightpiter.model.objects.Station;
import com.nightpiter.model.objects.Transfer;
import com.nightpiter.model.objects.Tunnel;

/**
 * Graph class suitable to use with Dijkstra's shortest path algorithm
 * 
 * Package protected
 * 
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
class Graph {

	/**
	 * array of vertices
	 */
	private TwoKeyHashMap<Station, Line, Vertex> verticesMap;

	/**
	 * public constructor
	 * 
	 * @param modelData
	 */
	public Graph(MetroModelData modelData) {
		logProfilerBegin("create new Graph instance");

		ArrayList<Station> stations = modelData.getStations();
		logProfilerBegin("create vertices for each station, total stations: "
				+ stations.size());
		this.verticesMap = new TwoKeyHashMap<Station, Line, Vertex>();
		TwoKeyHashMap<Station, Line, Vertex> verticesMap = this.verticesMap;
		for (Station station : stations) {
			ArrayList<Line> lines = station.getLines();
			for (Line line : lines) {
				verticesMap.put(station, line, new Vertex(station));
			}
		}
		logProfilerEnd("for each station cycle complete, totat vertices: "
				+ verticesMap.size());

		int edgesCount = 0;
		logProfilerBegin("create Edge for each transition");
		log("processing multiline stations pseudo-transitions");
		for (Station station : stations) {
			ArrayList<Line> lines = station.getLines();
			if (lines.size() > 1) {
				for (Line line : lines) {
					Vertex vertex = verticesMap.get(station, line);
					for (Line anotherLine : lines) {
						if (line != anotherLine) {
							new Edge(anotherLine, vertex, verticesMap.get(
									station, anotherLine));
							edgesCount++;
						}
					}
				}
			}
		}

		ArrayList<Tunnel> tunnels = modelData.getTunnels();
		log("processing tunnels, total tunnels: " + tunnels.size());
		for (Tunnel tunnel : tunnels) {
			Line line = tunnel.getLine();
			new Edge(tunnel, verticesMap.get(tunnel.getFromStation(), line),
					verticesMap.get(tunnel.getToStation(), line));
			edgesCount++;
		}

		ArrayList<Transfer> transfers = modelData.getTransfers();
		log("processing transfers, total transfers: " + transfers.size());
		for (Transfer transfer : transfers) {
			Station fromStation = transfer.getFromStation();
			Station toStation = transfer.getToStation();
			for (Line fromLine : fromStation.getLines()) {
				for (Line toLine : toStation.getLines()) {
					new Edge(transfer, verticesMap.get(fromStation, fromLine),
							verticesMap.get(toStation, toLine));
					edgesCount++;
				}
			}
		}

		logProfilerEnd("for each transition cycle complete, totat edges: "
				+ edgesCount);

		logProfilerEnd("graph contruct complete");
	}

	/**
	 * prepares graph to the new calculation
	 * 
	 * @param station
	 *            the initial position
	 */
	public void reset() {
		for (Vertex vertex : this.verticesMap.values()) {
			vertex.reset();
		}
	}

	/**
	 * @return the vertices
	 */
	public TwoKeyHashMap<Station, Line, Vertex> getVerticesHash() {
		return verticesMap;
	}

}
